﻿using AntShares.Compiler.JVM;
using AntShares.VM;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Numerics;
using System.Windows.Forms;

namespace testtool
{
    public partial class Form1 : Form, ILogger
    {
        public Form1()
        {
            InitializeComponent();
        }

        JavaClass module;
        ModuleConverter converter;
        private void Form1_Load(object sender, EventArgs e)
        {
            //一个快速使用的方法
            //byte[] code= AntShares.VM.ILConverter.Converter.Convert(dll);

            //load test dll
            var dll = System.IO.File.ReadAllBytes("go.class");

            var js = new javaloader.ClassFile(dll, 0, dll.Length);
            var src = System.IO.File.ReadAllLines("go.java");
            this.module = new JavaClass(js, src);
            //不需要pdb 传个null 就行，只是不会提示代码关联，没别的影响
            //this.module = new JavaClass(js,null);


            //转换为小蚁vmcode
            this.converter = new ModuleConverter(this);
            this.converter.Convert(this.module);

            this.richTextBox1.Text = this.converter.outModule.GenJson();
            System.IO.File.WriteAllText("testout.json", this.richTextBox1.Text);
            initTreeData();
        }

        private void initTreeData()
        {
            treeData.Nodes.Clear();
            //testdata
            //TreeNode root = new TreeNode("testdata");
            //treeData.Nodes.Add(root);

            //string[] files = System.IO.Directory.GetFiles("testdata", "*.txt");
            //foreach (var f in files)
            //{
            //    TreeNode sn = new TreeNode(System.IO.Path.GetFileName(f));
            //    sn.Tag = new CodeFile(f);
            //    root.Nodes.Add(sn);
            //}
            //ildata
            TreeNode

            root = new TreeNode("from testlib.dll");
            treeData.Nodes.Add(root);

            {
                TreeNode sn = new TreeNode(this.module.classfile.Name);
                sn.Tag = this.module;
                root.Nodes.Add(sn);
                //fields
                foreach (var f in this.module.fields)
                {
                    TreeNode ssn = new TreeNode(f.Key);
                    ssn.Tag = f.Value;
                    sn.Nodes.Add(ssn);
                }
                //methods
                foreach (var m in this.module.methods)
                {
                    if (m.Value.method == null) continue;
                    var pname = "()";
                    if (m.Value.paramTypes.Count > 0)
                    {
                        pname = "(..." + m.Value.paramTypes.Count + "ps)";
                    }
                    TreeNode ssn = new TreeNode(m.Key + pname);
                    ssn.Tag = m.Value;
                    sn.Nodes.Add(ssn);
                }
            }
            treeData.ExpandAll();
        }

        private void treeData_AfterSelect(object sender, TreeViewEventArgs e)
        {
            //CodeFile finfo = null;
            JavaMethod method = null;
            if (e.Node != null)
            {
                //finfo = e.Node.Tag as CodeFile;
                method = e.Node.Tag as JavaMethod;
            }
            //setFile(finfo);
            setMethod(method);

        }
        AntsMethod curCode;
        //CodeClip curCode;
        //private void setFile(CodeFile info)
        //{
        //    if (info == null)
        //    {
        //        this.textBytes.Text = "";
        //        this.listOpCode.Items.Clear();
        //        curCode = null;
        //    }
        //    else
        //    {
        //        var code = info.GetCode();
        //        curCode = new AntShares.VM.Tool.CodeClip();
        //        curCode.Parse(code);
        //        this.textBytes.Text = CodeFile.FormatCode(code);
        //        this.listOpCode.Items.Clear();
        //        foreach (var c in curCode.opcodes)
        //        {
        //            this.listOpCode.Items.Add(c);
        //        }
        //    }
        //}
        Dictionary<int, int> cacheLine = new Dictionary<int, int>();
        private void setMethod(JavaMethod method)
        {
            if (method == null || this.converter.methodLink.ContainsKey(method) == false)
            {
                this.listILCode.Items.Clear();
                this.listOpCode.Items.Clear();
            }
            else
            {
                this.listILCode.Items.Clear();
                this.listOpCode.Items.Clear();
                //临时变量
                this.listILCode.Items.Add("==Variables==");
                foreach (var a in method.method.LocalVariableTableAttribute)
                {
                    this.listILCode.Items.Add(a.name + " " + a.index + " " + a.start_pc + " " + a.descriptor);
                }
                this.listILCode.Items.Add(method.addLocal_VariablesCount);
                //foreach (var c in method.body_Variables)
                //{
                //    this.listILCode.Items.Add(c);
                //}
                this.listILCode.Items.Add("==body==" + "(下面的括号里面是代码行号)");

                foreach (var c in method.body_Codes.Values)
                {
                    this.listILCode.Items.Add(c);
                }

                //opcode
                var am = this.converter.methodLink[method];
                this.listOpCode.Items.Add("==Variables==");
                this.listOpCode.Items.Add(am.addVariablesCount);
                //foreach (var c in am.body_Variables)
                //{
                //    this.listOpCode.Items.Add(c);
                //}
                this.listOpCode.Items.Add("func=" + am.name);
                this.listOpCode.Items.Add("addr=" + am.funcaddr.ToString("X04"));

                this.curCode = am;


                //跳至代码行数
                richTextBox1.SelectionProtected = true;
                var indexpos = richTextBox1.Find("\"" + am.funcaddr.ToString("X04"));
                var linepos = richTextBox1.GetLineFromCharIndex(indexpos);
                var line = richTextBox1.Lines[linepos];
                var b = richTextBox1.GetFirstCharIndexFromLine(linepos);
                richTextBox1.Select(b, line.Length);
                richTextBox1.ScrollToCaret();
                //if (cacheLine.ContainsKey(am.funcaddr))
                //{
                //    var i = cacheLine[am.funcaddr];
                //    var line = richTextBox1.Lines[i];
                //    var b = richTextBox1.GetFirstCharIndexFromLine(i);

                //    richTextBox1.Select(b, line.Length);
                //    richTextBox1.ScrollToCaret();
                //}
                //else
                //{
                //    var addrstr = am.funcaddr.ToString("X04") + " ";
                //    for (var i = 0; i < richTextBox1.Lines.Length; i++)
                //    {
                //        var line = richTextBox1.Lines[i];
                //        if (line.IndexOf(addrstr) >= 0)
                //        {
                //            var b = richTextBox1.GetFirstCharIndexFromLine(i);

                //            richTextBox1.Select(b, line.Length);
                //            richTextBox1.ScrollToCaret();
                //            cacheLine[am.funcaddr] = i;
                //            break;
                //        }
                //    }
                //}
            }
        }
        class container : IScriptContainer
        {
            public byte[] GetMessage()
            {
                return new byte[] { 1, 2, 3, 4 };
            }

            public byte[] ToArray()
            {
                throw new NotImplementedException();
            }
        }
        class crypto : ICrypto
        {
            public byte[] Hash160(byte[] message)
            {
                return new byte[] { 4, 56, 66, 5 };
            }

            public byte[] Hash256(byte[] message)
            {
                throw new NotImplementedException();
            }

            public bool VerifySignature(byte[] message, byte[] signature, byte[] pubkey)
            {
                return true;
            }
        }
        ExecutionEngine getEngine()
        {
            var engine = new ExecutionEngine(new antshares.vmext.Transaction(), new crypto(), null, new antshares.vmext.testapiservice());
            this.engine = engine;
            return engine;
        }
        ExecutionEngine engine;

        private void button1_Click(object sender, EventArgs e)
        {//run button
            if (curCode == null)
                return;
            if (this.engine == null)
            {
                this.clearLog();
                this.engine = getEngine();

                for (var i = 0; i < curCode.paramtypes.Count; i++)
                {
                    var bn = new BigInteger(i * 100 + 99);
                    this.engine.EvaluationStack.Push(bn);
                }

                this.engine.LoadScript(this.converter.outModule.Build());
                this.engine.InvocationStack.Peek().InstructionPointer = curCode.funcaddr;

                this.Log("Start Engine:" + this.engine.State);
            }
            while (this.engine != null)
            {
                OneStep();
            }
        }

        void clearLog()
        {
            this.listConsole1.Items.Clear();
        }
        public void Log(string str)
        {
            this.listConsole1.Items.Add(str);
            this.listConsole1.SelectedIndex = this.listConsole1.Items.Count - 1;
        }

        private void button2_Click(object sender, EventArgs e)
        {//one step
            if (curCode == null)
                return;
            if (this.engine == null)
            {
                this.clearLog();
                this.engine = getEngine();

                for (var i = 0; i < curCode.paramtypes.Count; i++)
                {
                    var bn = new BigInteger(i * 100 + 99);
                    this.engine.EvaluationStack.Push(bn);
                }

                this.engine.LoadScript(this.converter.outModule.Build());
                this.engine.InvocationStack.Peek().InstructionPointer = curCode.funcaddr;
                this.Log("Start Engine:" + this.engine.State);
            }

            OneStep();

        }

        private void OneStep()
        {
            ExecutionContext context = engine.InvocationStack.Peek();

            this.Log("exe:(" + context.InstructionPointer.ToString("X02") + ")");

            var code = this.engine.CurrentContext.NextInstruction;
            this.engine.StepInto();
            this.Log("    " + code.ToString() + "   - " + this.engine.State);

            this.listValues.Items.Clear();
            foreach (StackItem v in engine.EvaluationStack)
            {
                if (v.IsArray)
                {
                    var vv = v.GetArray();
                    string outstr = "in stack:[";
                    foreach (var i in vv)
                    {
                        try
                        {
                            BigInteger iniv = i.GetBigInteger();
                            outstr += iniv.ToString() + ",";
                        }
                        catch
                        {
                            outstr += "?,";
                        }


                    }
                    this.listValues.Items.Add(outstr + "...]");
                }
                else
                {
                    try
                    {
                        BigInteger iniv = v.GetBigInteger();
                        this.listValues.Items.Add("in stack:" + iniv + "(0X" + iniv.ToString("X02") + ")");
                    }
                    catch
                    {
                        this.listValues.Items.Add("in stack:not number");

                    }
                }
            }
            listValuesAlt.Items.Clear();
            foreach (StackItem v in engine.AltStack)
            {
                if (v.IsArray)
                {
                    var vv = v.GetArray();
                    string outstr = "in stack:[";
                    foreach (var i in vv)
                    {
                        try
                        {
                            BigInteger iniv = i.GetBigInteger();
                            outstr += iniv.ToString() + ",";
                        }
                        catch
                        {
                            outstr += "?,";
                        }


                    }
                    this.listValuesAlt.Items.Add(outstr + "...]");
                }
                else
                {
                    try
                    {
                        BigInteger iniv = v.GetBigInteger();
                        this.listValuesAlt.Items.Add("in stack:" + iniv + "(0X" + iniv.ToString("X02") + ")");
                    }
                    catch
                    {
                        this.listValuesAlt.Items.Add("in stack:not number");

                    }
                }
            }
            if ((this.engine.State & VMState.FAULT) > 0)
            {
                this.Log("Engine FAULT End");
                this.engine = null;
                return;
            }
            if ((this.engine.State & VMState.HALT) > 0)
            {
                this.Log("Engine End");
                this.engine = null;
                return;
            }
        }

        private void textSrc_TextChanged(object sender, EventArgs e)
        {
            var src = textSrc.Text;
            var bytes = System.Text.Encoding.ASCII.GetBytes(src);
            string o = "";
            for (var i = 0; i < bytes.Length; i++)
            {
                o += bytes[i].ToString("X02");
            }
            textAscii.Text = o;

            textLength.Text = bytes.Length.ToString("X");
        }
    }
}
